﻿using NLua;
using OptimizationPlus.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using TaleWorlds.Core;
using TaleWorlds.Engine;
using TaleWorlds.Library;
using TaleWorlds.MountAndBlade;

namespace OptimizationPlus.Behaviors
{
    public class LuaMissionBehavior : MissionBehavior
    {
        private readonly LuaTable _table;
        private readonly ConcurrentQueue<LuaFunction> _timeouts = new ConcurrentQueue<LuaFunction>();

        public LuaMissionBehavior(LuaTable table)
        {
            _table = table;
        }

        public override MissionBehaviorType BehaviorType => _table["behaviorType"] is MissionBehaviorType type ? type : MissionBehaviorType.Other;

        public override void OnAfterMissionCreated()
        {
            _table.InvokeFunc("onAfterMissionCreated");
        }

        public override void OnBehaviorInitialize()
        {
            _table.InvokeFunc("onBehaviorInitialize");
        }

        public override void OnCreated()
        {
            _table.InvokeFunc("onCreated");
        }

        public override void EarlyStart()
        {
            _table.InvokeFunc("earlyStart");
        }

        public override void AfterStart()
        {
            _table.InvokeFunc("afterStart");
        }

        public override void OnMissileHit(Agent attacker, Agent victim, bool isCanceled, AttackCollisionData collisionData)
        {
            _table.InvokeFunc("onMissileHit", attacker, victim, isCanceled);
        }

        public override void OnMeleeHit(Agent attacker, Agent victim, bool isCanceled, AttackCollisionData collisionData)
        {
            _table.InvokeFunc("onMeleeHit", attacker, isCanceled, collisionData);
        }

        public override void OnMissileCollisionReaction(Mission.MissileCollisionReaction collisionReaction, Agent attackerAgent, Agent attachedAgent, sbyte attachedBoneIndex)
        {
            _table.InvokeFunc("onMissileCollisionReaction", collisionReaction, attackerAgent, attachedAgent, attachedBoneIndex);
        }

        public override void OnMissionScreenPreLoad()
        {
            _table.InvokeFunc("onMissionScreenPreLoad");
        }

        public override void OnAgentCreated(Agent agent)
        {
            _table.InvokeFunc("onAgentCreated", agent);
        }

        public override void OnAgentBuild(Agent agent, Banner banner)
        {
            _table.InvokeFunc("onAgentBuild", agent, banner);
        }

        public override void OnAgentTeamChanged(Team prevTeam, Team newTeam, Agent agent)
        {
            _table.InvokeFunc("onAgentTeamChanged", prevTeam, newTeam, agent);
        }

        public override void OnAgentControllerSetToPlayer(Agent agent)
        {
            _table.InvokeFunc("onAgentControllerSetToPlayer", agent);
        }

        public override void OnAgentHit(Agent affectedAgent, Agent affectorAgent, in MissionWeapon affectorWeapon, in Blow blow, in AttackCollisionData attackCollisionData)
        {
            _table.InvokeFunc("onAgentHit", affectedAgent, affectorAgent, affectorWeapon, blow, attackCollisionData);
        }

        public override void OnScoreHit(Agent affectedAgent, Agent affectorAgent, WeaponComponentData attackerWeapon, bool isBlocked, bool isSiegeEngineHit, in Blow blow, in AttackCollisionData collisionData, float damagedHp, float hitDistance, float shotDifficulty)
        {
            _table.InvokeFunc("onScoreHit", affectedAgent, affectorAgent, attackerWeapon, isBlocked, isSiegeEngineHit, blow, collisionData, damagedHp, hitDistance, shotDifficulty);
        }

        public override void OnEarlyAgentRemoved(Agent affectedAgent, Agent affectorAgent, AgentState agentState, KillingBlow blow)
        {
            _table.InvokeFunc("onEarlyAgentRemoved", affectedAgent, affectorAgent, agentState, blow);
        }

        public override void OnAgentRemoved(Agent affectedAgent, Agent affectorAgent, AgentState agentState, KillingBlow blow)
        {
            _table.InvokeFunc("onAgentRemoved",  affectedAgent, affectorAgent, agentState, blow);
        }

        public override void OnAgentDeleted(Agent affectedAgent)
        {
            _table.InvokeFunc("onAgentDeleted", affectedAgent);
        }

        public override void OnAgentFleeing(Agent affectedAgent)
        {
            _table.InvokeFunc("onAgentFleeing", affectedAgent);
        }

        public override void OnAgentPanicked(Agent affectedAgent)
        {
            _table.InvokeFunc("onAgentPanicked", affectedAgent);
        }

        public override void OnFocusGained(Agent agent, IFocusable focusableObject, bool isInteractable)
        {
            _table.InvokeFunc("onFocusGained", agent, focusableObject, isInteractable);
        }

        public override void OnFocusLost(Agent agent, IFocusable focusableObject)
        {
            _table.InvokeFunc("onFocusLost", agent, focusableObject);
        }

        public override void OnAddTeam(Team team)
        {
            _table.InvokeFunc("onAddTeam", team);
        }

        public override void AfterAddTeam(Team team)
        {
            _table.InvokeFunc("afterAddTeam", team);
        }

        public override void OnAgentInteraction(Agent userAgent, Agent agent)
        {
            _table.InvokeFunc("onAgentInteraction", userAgent, agent);
        }

        public override void OnClearScene()
        {
            _table.InvokeFunc("onClearScene");
        }

        protected override void OnEndMission()
        {
            _table.InvokeFunc("onEndMission");
        }

        public override void OnRemoveBehavior()
        {
            _table.InvokeFunc("onRemoveBehavior");
        }

        public override void OnPreMissionTick(float dt)
        {
            _table.InvokeFunc("onPreMissionTick", dt);
        }

        public override void OnPreDisplayMissionTick(float dt)
        {
            _table.InvokeFunc("onPreDisplayMissionTick", dt);
        }

        public override void OnMissionTick(float dt)
        {
            _table.InvokeFunc("onMissionTick", dt);
            try
            {
                if (_timeouts.TryDequeue(out var timeout))
                {
                    timeout.Call(dt);
                }
            }
            catch (Exception ex)
            {
                string msg = "[Lua] error: " + ex.ToString();
                Debug.Print(msg, 2, Debug.DebugColor.Red);
                InformationManager.DisplayMessage(new InformationMessage(msg, Colors.Red));
            }
        }

        public override void OnAgentMount(Agent agent)
        {
            _table.InvokeFunc("onAgentMount", agent);
        }

        public override void OnAgentDismount(Agent agent)
        {
            _table.InvokeFunc("onAgentDismount", agent);
        }

        public override bool IsThereAgentAction(Agent userAgent, Agent otherAgent)
        {
            return _table.InvokeFunc("isThereAgentAction", userAgent, otherAgent)[0] is bool b && b;
        }

        public override void OnEntityRemoved(GameEntity entity)
        {
            _table.InvokeFunc("onEntityRemoved", entity);
        }

        public override void OnObjectUsed(Agent userAgent, UsableMissionObject usedObject)
        {
            _table.InvokeFunc("onObjectUsed", userAgent, usedObject);
        }

        public override void OnObjectStoppedBeingUsed(Agent userAgent, UsableMissionObject usedObject)
        {
            _table.InvokeFunc("onObjectStoppedBeingUsed", userAgent, usedObject);
        }

        public override void OnRenderingStarted()
        {
            _table.InvokeFunc("onRenderingStarted");
        }

        public override void OnMissionStateActivated()
        {
            _table.InvokeFunc("onMissionStateActivated");
        }

        public override void OnMissionStateFinalized()
        {
            _table.InvokeFunc("onMissionStateFinalized");
        }

        public override void OnMissionStateDeactivated()
        {
            _table.InvokeFunc("onMissionStateDeactivated");
        }

        public override List<CompassItemUpdateParams> GetCompassTargets()
        {
            return _table.InvokeFunc("getCompassTargets")[0] is List<CompassItemUpdateParams> list ? list : null;
        }

        public override void OnAssignPlayerAsSergeantOfFormation(Agent agent)
        {
            _table.InvokeFunc("onAssignPlayerAsSergeantOfFormation", agent);
        }

        public override void OnDeploymentFinished()
        {
            _table.InvokeFunc("onDeploymentFinished");
        }

        public override void OnTeamDeployed(Team team)
        {
            _table.InvokeFunc("onTeamDeployed", team);
        }

        protected override void OnGetAgentState(Agent agent, bool usedSurgery)
        {
            _table.InvokeFunc("onGetAgentState", agent, usedSurgery);
        }

        public override void OnAgentAlarmedStateChanged(Agent agent, Agent.AIStateFlag flag)
        {
            _table.InvokeFunc("onAgentAlarmedStateChanged", agent, flag);
        }

        protected override void OnObjectDisabled(DestructableComponent destructionComponent)
        {
            _table.InvokeFunc("onObjectDisabled", destructionComponent);
        }

        public override void OnMissionModeChange(MissionMode oldMissionMode, bool atStart)
        {
            _table.InvokeFunc("onMissionModeChange", oldMissionMode, atStart);
        }

        protected override void OnAgentControllerChanged(Agent agent, Agent.ControllerType oldController)
        {
            _table.InvokeFunc("onAgentControllerChanged", agent, oldController);
        }

        public override void OnRegisterBlow(Agent attacker, Agent victim, GameEntity realHitEntity, Blow b, ref AttackCollisionData collisionData, in MissionWeapon attackerWeapon)
        {
            _table.InvokeFunc("onRegisterBlow", attacker, victim, realHitEntity, b, collisionData, attackerWeapon);
        }

        public override void OnAgentShootMissile(Agent shooterAgent, EquipmentIndex weaponIndex, Vec3 position, Vec3 velocity, Mat3 orientation, bool hasRigidBody, int forcedMissileIndex)
        {
            _table.InvokeFunc("onAgentShootMissile", shooterAgent, weaponIndex, position, velocity, orientation, hasRigidBody, forcedMissileIndex);
        }

        public void SetTimeout(int millisecondsDelay, LuaFunction func)
        {
            Task.Run(async () =>
            {
                await Task.Delay(millisecondsDelay);
                _timeouts.Enqueue(func);
            });
        }
    }
}
